﻿using System;
using System.Linq;
using System.Reflection;
using LoungeRepo.Contracts.Normalizer;
using LoungeRepo.Normalizer;
using LoungeRepo.Normalizer.DataStructures;
using NUnit.Framework;

namespace Test.LoungeRepo.Normalizer
{
    [TestFixture]
    public class testObjectGraphNormalizer
    {
        [Test]
        public void Normalize_single_object()
        {
            var sut = new ObjectGraphNormalizer();

            var o = new Simple {Text = "hello, world" };
            var no = sut.NormalizeNonEntity(o);

            Assert.AreEqual(1, no.Fields.Count());
            Assert.AreEqual("Text", no.Fields.First().Name);
            Assert.AreEqual("hello, world", no.Fields.First().Value);
        }


        [Test]
        public void Process_null_references()
        {
            var sut = new ObjectGraphNormalizer();

            var o = new Simple { Text = null };
            var no = sut.NormalizeNonEntity(o);

            Assert.IsNull(no.Fields.First().Value);
        }


        [Test]
        public void Ignore_transient_fields()
        {
            var sut = new ObjectGraphNormalizer();

            var no = sut.NormalizeNonEntity(new Node
                        {
                            Text = "hello, world",
                            Comment = "ignore this"
                        });

            Assert.AreEqual(2, no.Fields.Count());

            Assert.AreEqual("Text", no.Fields.ElementAt(0).Name);
            Assert.AreEqual("Child", no.Fields.ElementAt(1).Name);
        }


        [Test]
        public void Normalize_object_graph()
        {
            var sut = new ObjectGraphNormalizer();

            var o = new Node{Text = "parent"};
            var oChild = new Node {Text = "child"};
            o.Child = oChild;

            var no = sut.NormalizeNonEntity(o);

            INormalizedObject noChild = (INormalizedObject)no.Fields.ElementAt(1).Value;
            Assert.AreEqual("child", noChild.Fields.First().Value);
        }

        
        [Test]
        public void Normalize_cyclic_graph()
        {
            var sut = new ObjectGraphNormalizer();

            var o = new Node {Text = "parent"};
            var oChild = o;
            o.Child = oChild; // cyclic reference

            NormalizedObject no = (NormalizedObject)sut.NormalizeNonEntity(o);

            NormalizedObjectReference noChild = (NormalizedObjectReference)no.Fields.ElementAt(1).Value;
            Assert.AreEqual(no.RefId, noChild.RefId);
        }


        [Test]
        public void Normalize_multiple_refs_to_same_entity()
        {
            EntityNode e = new EntityNode {Id = "1", Partition = ""};
            var o = new ObjectReferencingEntities();
            o.E1 = e;
            o.E2 = e;

            var sut = new ObjectGraphNormalizer();
            var no = sut.NormalizeNonEntity(o);

            Assert.IsInstanceOfType(typeof(NormalizedEntityReference),  no.Fields.Single(f => f.Name == "E1").Value);
            Assert.IsInstanceOfType(typeof(NormalizedEntityReference), no.Fields.Single(f => f.Name == "E2").Value);
        }


        [Test]
        public void Process_scalar_types()
        {
            var sut = new ObjectGraphNormalizer();

            var o = new Scalars {i = 99, d = 3.14, c = 314.1592m, b = true };
            var no = sut.NormalizeNonEntity(o);

            Assert.AreEqual(99, GetFieldValueByName(no, "i"));
            Assert.AreEqual(3.14, GetFieldValueByName(no, "d"));
            Assert.AreEqual(314.1592m, GetFieldValueByName(no, "c"));
            Assert.IsTrue((bool)GetFieldValueByName(no, "b"));
        }


        [Test]
        public void Process_special_types()
        {
            var sut = new ObjectGraphNormalizer();

            var o = new SpecialSimpleTypes{ dt = new DateTime(2009, 08, 23), e=BindingFlags.Instance | BindingFlags.Public };
            var no = sut.NormalizeNonEntity(o);

            Assert.AreEqual(new DateTime(2009, 8, 23), GetFieldValueByName(no, "dt"));
            Assert.AreEqual(BindingFlags.Instance | BindingFlags.Public, GetFieldValueByName(no, "e"));
        }


        [Test]
        public void Normalize_single_dim_array()
        {
            string[] array = { "a", "b", "c" };

            var sut = new ObjectGraphNormalizer();
            var no = sut.NormalizeArray(array);

            Assert.AreEqual(array.GetType(), no.ObjectType);
            Assert.AreEqual("_arrayRank", no.Fields.First().Name);
            Assert.AreEqual(1, no.Fields.First().Value);
            Assert.AreEqual(3, no.Fields.ElementAt(1).Value);
            Assert.AreEqual(5, no.Fields.Count());
        }


        [Test]
        public void Normalize_rectangular_array()
        {
            int[,] array = new int[,] { { 1, 11 }, { 2, 22 }, { 3, 33 } };

            var sut = new ObjectGraphNormalizer();
            var no = sut.NormalizeArray(array);

            Assert.AreEqual(array.GetType(), no.ObjectType);
            Assert.AreEqual("_arrayRank", no.Fields.First().Name);
            Assert.AreEqual(2, no.Fields.First().Value);
            Assert.AreEqual(3, no.Fields.ElementAt(1).Value);
            Assert.AreEqual(2, no.Fields.ElementAt(2).Value);
            Assert.AreEqual(9, no.Fields.Count());
        }


        [Test]
        public void Normalize_jagged_arrays()
        {
            int[][] array = new int[2][];
            array[0] = new int[] {1};
            array[1] = new int[] { 2, 22 };

            var sut = new ObjectGraphNormalizer();
            var no = sut.NormalizeArray(array);

            Assert.AreEqual(array.GetType(), no.ObjectType);
            Assert.AreEqual("_arrayRank", no.Fields.First().Name);
            Assert.AreEqual(1, no.Fields.First().Value);
            Assert.AreEqual(4, no.Fields.Count());

            no = (NormalizedObject)no.Fields.Last().Value;
            Assert.AreEqual(array[1].GetType(), no.ObjectType);
            Assert.AreEqual("_arrayRank", no.Fields.First().Name);
            Assert.AreEqual(1, no.Fields.First().Value);
            Assert.AreEqual(4, no.Fields.Count());
        }

        
        [Test, Ignore]
        public void Normalize_struct()
        {}


        [Test, Ignore]
        public void Normalize_subclass()
        {}


        private object GetFieldValueByName(INormalizedObject source, string fieldname)
        {
            return source.Fields.Single(f => f.Name == fieldname).Value;
        }
    }
}
